package com.wuliu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wuliu.common.constant.CacheConstants;
import com.wuliu.common.constant.Constants;
import com.wuliu.common.core.domain.entity.BasCust;
import com.wuliu.common.core.domain.entity.SysDept;
import com.wuliu.common.core.redis.RedisCache;
import com.wuliu.common.exception.ServiceException;
import com.wuliu.common.utils.*;
import com.wuliu.common.utils.ip.IpUtils;
import com.wuliu.system.domain.SysAppletMenu;
import com.wuliu.system.domain.SysAppletRoleMenu;
import com.wuliu.common.core.domain.entity.SysAppletUser;
import com.wuliu.system.domain.dto.LoginDto;
import com.wuliu.system.domain.vo.BasCustVo;
import com.wuliu.system.domain.vo.SysAppletUserVo;
import com.wuliu.system.mapper.*;
import com.wuliu.system.service.ISysSmsRecordService;
import com.wuliu.system.service.LoginService;
import com.wuliu.system.service.AppTokenService;
import lombok.RequiredArgsConstructor;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author realue
 * @version 1.0
 * @date 2022-10-14 11:05
 */
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    private final SysAppletUserMapper sysAppletUserMapper;
    private final AppTokenService appTokenService;
    private final SysAppletRoleMenuMapper sysAppletRoleMenuMapper;
    private final SysAppletMenuMapper sysAppletMenuMapper;
    private final SysDeptMapper sysDeptMapper;
    private final BasCustMapper basCustMapper;
    private final RedisCache redisCache;
    private final ISysSmsRecordService sysSmsRecordService;

    /**
     * 用户登录
     *
     * @param loginDto
     * @return
     */
    @Override
    public SysAppletUserVo login(LoginDto loginDto) {
        SysAppletUserVo retVo = new SysAppletUserVo();
        if (loginDto.getType() == 1 && StrUtil.isEmpty(loginDto.getPwd()))
            throw new ServiceException("密码不能为空");
        if (loginDto.getType() == 2 && StrUtil.isEmpty(loginDto.getCode()))
            throw new ServiceException("验证码不能空");
        SysAppletUser user = sysAppletUserMapper.selectOne(new LambdaQueryWrapper<SysAppletUser>()
                .eq(SysAppletUser::getUserNo, loginDto.getUserNo())
                .eq(SysAppletUser::getSysAccount, loginDto.getSysAccount()));
        if (null == user) {
            throw new ServiceException("用户名不存在");
        }
        if (loginDto.getType() == 1) {
            if (!SecurityUtils.matchesPassword(loginDto.getPwd(), user.getPassword())) {
                throw new ServiceException("密码错误");
            }
        } else if (loginDto.getType() == 2) {
            if (!appTokenService.checkVerificationCode(loginDto.getUserNo(), loginDto.getCode())) {
                throw new ServiceException("验证码错误或已过期");
            }
        }
        BeanUtil.copyProperties(user, retVo);
        String token = appTokenService.generateToken(user);
        retVo.setToken(token);
        List<SysAppletMenu> menus = new ArrayList<>();
        if (!Objects.isNull(user.getRoleId())) {
            List<SysAppletRoleMenu> roleMenus = sysAppletRoleMenuMapper.selectList(new LambdaQueryWrapper<SysAppletRoleMenu>().eq(SysAppletRoleMenu::getRoleId, user.getRoleId()));
            if (StringUtils.isNotEmpty(roleMenus)) {
                List<Long> menuIds = roleMenus.stream().map(SysAppletRoleMenu::getMenuId).collect(Collectors.toList());
                QueryWrapper<SysAppletMenu> wrapper = new QueryWrapper<>();
                wrapper.in("id", menuIds).orderBy(true, true, "order_num");
                menus = sysAppletMenuMapper.selectList(wrapper);
            }
        }
        retVo.setMenus(menus);
        SysDept sysDept = sysDeptMapper.selectDeptById(user.getDeptId());
        if (null != sysDept) {
            retVo.setDeptName(sysDept.getDeptName());
            retVo.setDeptNo(sysDept.getDeptNo());
        }
        logger.info("登录成功:{}", JSONUtil.toJsonStr(retVo));
        updateUserLoginInfo(user.getUserId());
        return retVo;
    }

    /**
     * 更新用户登录时间
     *
     * @param userId
     */
    private void updateUserLoginInfo(Long userId) {
        SysAppletUser sysUser = new SysAppletUser();
        sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        sysUser.setLoginDate(DateUtils.getNowDate());
        sysAppletUserMapper.update(sysUser, new LambdaQueryWrapper<SysAppletUser>().eq(SysAppletUser::getUserId, userId));
    }

    /**
     * 发送验证码
     *
     * @param mobile
     * @param sysAccount
     * @return
     */
    @Override
    public Boolean sendSms(String mobile, String sysAccount) {
        if (StringUtils.isEmpty(mobile)) {
            throw new ServiceException("手机号码不能为空");
        }
        if (!Validator.isMobile(mobile)) {
            throw new ServiceException("手机号码格式不正确");
        }
        String code = RandomUtil.randomNumbers(6);
        if (StringUtils.isEmpty(sysAccount)) {
            sysAccount = Constants.DEFAULT_SYSACCOUNT;
        }
        String content = String.format(Constants.LOGIN_SMS_TEMPLATE, code);
        sysSmsRecordService.addSms(mobile, content, sysAccount);
        appTokenService.saveVerificationCode(mobile, code);
        logger.info("验证码发送成功:{}", code);
        return true;
    }

    /**
     * 小程序登录
     *
     * @param mobile
     * @param code
     * @param sysAccount
     * @return
     */
    @Override
    public BasCustVo login(String mobile, String code, String sysAccount) {
        if (!appTokenService.checkVerificationCode(mobile, code)) {
            throw new ServiceException("验证码错误或已过期");
        }
        BasCustVo retCust = new BasCustVo();
        BasCust basCust = basCustMapper.selectOne(new LambdaQueryWrapper<BasCust>()
                .eq(BasCust::getSysAccount, sysAccount)
                .eq(BasCust::getMobile, mobile));
        if (null == basCust) {
            basCust = new BasCust();
            basCust.setId(IdUtil.randomUUID());
            basCust.setMobile(mobile);
            basCust.setCreateTime(DateUtils.getNowDate());
            basCust.setCreateBy("system");
            basCust.setSysAccount(sysAccount);
            basCustMapper.insertBasCust(basCust);
        }
        BeanUtil.copyProperties(basCust, retCust);
        String token = IdUtil.randomUUID();
        redisCache.setCacheObject(CacheConstants.TOKEN_WEIXIN_LOGIN_INFO_KEY + token, JSONUtil.toJsonStr(basCust), 7, TimeUnit.DAYS);
        retCust.setToken(token);
        return retCust;
    }

    /**
     * 账号密码登录
     *
     * @param loginNo
     * @param password
     * @param sysAccount
     * @return
     */
    @Override
    public BasCustVo loginByPwd(String loginNo, String password, String sysAccount) {
        if (StringUtils.isAnyBlank(loginNo, password)) {
            throw new ServiceException("用户密码不能为空");
        }
        BasCust basCust = basCustMapper.selectOne(new LambdaQueryWrapper<BasCust>()
                .eq(BasCust::getSysAccount, sysAccount)
                .eq(BasCust::getLoginNo, loginNo));
        if (null == basCust) {
            throw new ServiceException("用户名不存在");
        }
        if (!SecurityUtils.matchesPassword(password, basCust.getPassword())) {
            throw new ServiceException("密码错误");
        }
        BasCustVo retCust = new BasCustVo();
        BeanUtil.copyProperties(basCust, retCust);
        String token = IdUtil.randomUUID();
        redisCache.setCacheObject(CacheConstants.TOKEN_WEIXIN_LOGIN_INFO_KEY + token, JSONUtil.toJsonStr(basCust), 7, TimeUnit.DAYS);
        retCust.setToken(token);
        return retCust;
    }
}
